package service

import (
	"bytes"
	"gitee.com/crack007/api-throttling/config"
	"gitee.com/crack007/goose/throttle"
	"github.com/gin-gonic/gin"
	"io"
	"net"
	"net/http"
	"strconv"
	"time"
)

type apiService struct {
	Route         *config.RouteConfig
	ApiThrottle   throttle.IThrottle
	client        *http.Client
	responseQueue chan *Response
}

func NewApiService(route *config.RouteConfig) *apiService {
	var windowThrottle throttle.IThrottle
	if route.Strategy == 1 {
		windowThrottle = throttle.NewTokenThrottle(time.Duration(route.Unit) * time.Millisecond)
	} else {
		windowThrottle = throttle.NewWindowThrottle(time.Duration(route.Unit)*time.Millisecond, 60*time.Millisecond)
	}
	windowThrottle.Init()
	transport := &http.Transport{
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second, //连接超时
			KeepAlive: 30 * time.Second, //探活时间
		}).DialContext,
		MaxIdleConns:          30,               //最大空闲连接
		IdleConnTimeout:       90 * time.Second, //空闲超时时间
		TLSHandshakeTimeout:   10 * time.Second, //tls握手超时时间
		ExpectContinueTimeout: 1 * time.Second,  //100-continue状态码超时时间
	}
	client := &http.Client{
		Transport: transport,
	}
	return &apiService{
		Route:       route,
		ApiThrottle: windowThrottle,
		client:      client,
	}
}

func (s *apiService) call(ginCtx *gin.Context, respChan chan<- *Response) {
	var err error
	resp := &Response{}
	defer func() {
		resp.err = err
		respChan <- resp
	}()
	data, err := ginCtx.GetRawData()
	if err != nil {
		return
	}
	request, err := http.NewRequest(s.Route.Method, s.Route.Url, bytes.NewReader(data))
	if err != nil {
		return
	}
	client := http.Client{}
	response, err := client.Do(request)
	if err != nil {
		return
	}
	defer response.Body.Close()
	respData, err := io.ReadAll(response.Body)
	if err != nil {
		return
	}
	resp.contentType = response.Header.Get("content-type")
	resp.Data = respData
	resp.statusCode = response.StatusCode

}
func (s *apiService) HandleRequest(ctx *gin.Context) {
	ginCtx := ctx.Copy()
	respChan := make(chan *Response)
	go func() {
		uid, _ := ginCtx.GetQuery("_uid")
		id, _ := strconv.Atoi(uid)
		s.ApiThrottle.Call(int64(id), func() {
			go s.call(ginCtx, respChan)
		}, func() {
			c := s.Route.RejectConfig
			respChan <- &Response{
				statusCode:  c.Code,
				contentType: c.ContentType,
				Data:        []byte(c.Body),
			}
		})
	}()
	resp := <-respChan
	if resp.statusCode != 200 {
		ctx.AbortWithStatus(resp.statusCode)
		return
	}
	ctx.Data(resp.statusCode, resp.contentType, resp.Data)

}
